package com.example.esdemo.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;

/**
 * @author zhanglei
 * @date 2021-09-22
 */
@Api(tags = "书籍接口")
@RestController
@RequestMapping("/book")
public class BookController {

    @Autowired
    private TransportClient client;

    @GetMapping("/novel/getById")
    @ApiOperation(httpMethod = "GET", value = "根据id查询")
    public ResponseEntity get(@RequestParam(name = "id", defaultValue = "") String id) {
        if (id.isEmpty()) {
            return new ResponseEntity(HttpStatus.NOT_FOUND);
        }
        GetResponse result = this.client.prepareGet("book", "novel", id)
                .get();
        if (!result.isExists()) {
            return new ResponseEntity(HttpStatus.NOT_FOUND);
        }
        return new ResponseEntity(result.getSource(), HttpStatus.OK);
    }


    @PostMapping("/novel/add")
    @ApiOperation(httpMethod = "POST", value = "添加")
    public ResponseEntity add(@RequestParam(name = "title") String title,
                              @RequestParam(name = "author") String author,
                              @RequestParam(name = "word_count") int wordCount,
                              @RequestParam(name = "publish_date") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") String publishDate) {

        try {
            XContentBuilder content = XContentFactory.jsonBuilder()
                    .startObject()
                    .field("title", title)
                    .field("author", author)
                    .field("word_count", wordCount)
                    .field("publish_date", publishDate)
                    .endObject();

            IndexResponse result = this.client.prepareIndex("book", "novel")
                    .setSource(content)
                    .get();

            return new ResponseEntity(result.getId(), HttpStatus.OK);
        } catch (IOException e) {
            e.printStackTrace();
            return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    @DeleteMapping("/novel/delete")
    @ApiOperation(httpMethod = "DELETE", value = "删除")
    public ResponseEntity delete(@RequestParam(name = "id") String id) {

        DeleteResponse result = this.client.prepareDelete("book", "novel", id)
                .get();

        return new ResponseEntity(result.getResult().toString(), HttpStatus.OK);

    }

    @PostMapping("/novel/update")
    @ApiOperation(httpMethod = "POST", value = "更新")
    public ResponseEntity update(@RequestParam(name = "id") String id,
                                 @RequestParam(name = "title", required = false) String title,
                                 @RequestParam(name = "word_count", required = false) Integer wordCount,
                                 @RequestParam(name = "publish_date", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") String publishDate,
                                 @RequestParam(name = "author", required = false) String author) {

        try {
            XContentBuilder builder = XContentFactory.jsonBuilder().startObject();
            if (title != null) {
                builder.field("title", title);
            }
            if (author != null) {
                builder.field("author", author);
            }
            if (wordCount != null) {
                builder.field("word_count", wordCount);
            }
            if (publishDate != null) {
                builder.field("publish_date", publishDate);
            }
            builder.endObject();
            UpdateRequest update = new UpdateRequest("book", "novel", id);
            update.doc(builder);

            UpdateResponse result = this.client.update(update).get();

            return new ResponseEntity(result.getResult().toString(), HttpStatus.OK);
        } catch (Exception e) {
            e.printStackTrace();
            return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    @PostMapping("/novel/query")
    @ApiOperation(httpMethod = "POST", value = "复合查询")
    public ResponseEntity add(@RequestParam(name = "title", required = false) String title,
                              @RequestParam(name = "author", required = false) String author,
                              @RequestParam(name = "gt_word_count", required = false) Integer gtWordCount,
                              @RequestParam(name = "lt_word_count", required = false) Integer ltWordCount) {

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        if (title != null) {
            boolQuery.must(QueryBuilders.matchQuery("title", title));
        }
        if (author != null) {
            boolQuery.must(QueryBuilders.matchQuery("author", author));
        }

        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("word_count")
                .from(gtWordCount);

        if (ltWordCount != null && ltWordCount > 0) {
            rangeQuery.to(ltWordCount);
        }
        boolQuery.filter(rangeQuery);

        SearchRequestBuilder builder = this.client.prepareSearch("book")
                .setTypes("novel")
                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                .setQuery(boolQuery)
                .setFrom(0)
                .setSize(10);

        System.out.println(builder);

        SearchResponse response = builder.get();
        ArrayList<Map<String, Object>> result = new ArrayList<>();
        for (SearchHit hit : response.getHits()) {
            result.add(hit.getSourceAsMap());
        }

        return new ResponseEntity(result, HttpStatus.OK);

    }

    /**
     * From,Size分页查询，默认只能查1万内的数据，不允许深度分页
     * 太深的分页也没有意义
     *
     * @param title
     * @param author
     * @param pageNum  页数
     * @param pageSize 每页条数
     * @return ResponseEntity
     */
    @GetMapping("/novel/list")
    @ApiOperation(httpMethod = "GET", value = "分页查询")
    public ResponseEntity getList(@RequestParam(name = "title", required = false) String title,
                                  @RequestParam(name = "author", required = false) String author,
                                  @RequestParam(name = "pageNum", required = false, defaultValue = "1") Integer pageNum,
                                  @RequestParam(name = "pageSize", required = false, defaultValue = "3") Integer pageSize) {

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        if (title != null) {
            boolQuery.must(QueryBuilders.matchQuery("title", title));
        }
        if (author != null) {
            boolQuery.must(QueryBuilders.matchQuery("author", author));
        }
        int from = pageSize * (pageNum - 1);
        SearchRequestBuilder builder = this.client.prepareSearch("book")
                .setTypes("novel")
                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                .setQuery(boolQuery)
                .setFrom(from)
                .setSize(pageSize);

        System.out.println(builder);

        SearchResponse response = builder.get();
        ArrayList<Map<String, Object>> result = new ArrayList<>();
        for (SearchHit hit : response.getHits()) {
            result.add(hit.getSourceAsMap());
        }

        return new ResponseEntity(result, HttpStatus.OK);
    }

    /**
     * 游标滚动分页，可以查到所有数据(不建议使用)
     * 该方法是从第一页开始往后查到想要的那一页。
     *
     * @param title
     * @param author
     * @param pageNum  页数
     * @param pageSize 每页条数
     * @return
     */
    @GetMapping("/novel/list/rock")
    @ApiOperation(httpMethod = "GET", value = "游标滚动分页查询")
    public ResponseEntity getListRock(@RequestParam(name = "title", required = false) String title,
                                      @RequestParam(name = "author", required = false) String author,
                                      @RequestParam(name = "pageNum", required = false, defaultValue = "1") Integer pageNum,
                                      @RequestParam(name = "pageSize", required = false, defaultValue = "3") Integer pageSize) {

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        if (title != null) {
            boolQuery.must(QueryBuilders.matchQuery("title", title));
        }
        if (author != null) {
            boolQuery.must(QueryBuilders.matchQuery("author", author));
        }

        TimeValue timeValue = new TimeValue(60000);
        SearchRequestBuilder builder = this.client.prepareSearch("book")
                .setTypes("novel")
                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                .setQuery(boolQuery)
                .setFrom(0)
                .setSize(pageSize)
                .setScroll(timeValue);

        System.out.println(builder);
        SearchResponse response = builder.get();

        int i = 1;
        ArrayList<Map<String, Object>> result = new ArrayList<>();
        while (true) {

            //查到数据为空，无数据了为止。
            if (response.getHits().getHits().length == 0) {
                break;
            }
            //返回想要页面的数据
            if (i == pageNum) {
                for (SearchHit hit : response.getHits()) {
                    Iterator<Map.Entry<String, Object>> iterator = hit.getSourceAsMap().entrySet().iterator();
                    while (iterator.hasNext()) {
                        Map.Entry<String, Object> next = iterator.next();
                        System.out.println(next.getKey() + ": " + next.getValue());
                    }
                    result.add(hit.getSourceAsMap());
                }
                break;
            }
            //使用上次查询得的游标ScrollId，接着查。
            response = this.client.prepareSearchScroll(response.getScrollId())
                    .setScroll(timeValue).get();
            i++;

        }
        return new ResponseEntity(result, HttpStatus.OK);
    }

    /**
     * 查询下一页
     * 将ScrollId返回，查询下一页将ScrollId带进来。适用于下滑刷出来下一页(例如微博)，不允许跳页
     *
     * @param title
     * @param author
     * @param scrollId
     * @param pageSize
     * @return
     */
    @GetMapping("/novel/nextpage")
    @ApiOperation(httpMethod = "GET", value = "游标滚动分页查询下一页")
    public ResponseEntity getNextPage(@RequestParam(name = "title", required = false) String title,
                                      @RequestParam(name = "author", required = false) String author,
                                      @RequestParam(name = "scrollId", required = false, defaultValue = "") String scrollId,
                                      @RequestParam(name = "pageSize", required = false, defaultValue = "3") Integer pageSize) {

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        if (title != null) {
            boolQuery.must(QueryBuilders.matchQuery("title", title));
        }
        if (author != null) {
            boolQuery.must(QueryBuilders.matchQuery("author", author));
        }

        TimeValue timeValue = new TimeValue(60000);

        SearchResponse response = null;
        //scrollId为空，查第一页
        if (StringUtils.isEmpty(scrollId)) {
            SearchRequestBuilder builder = this.client.prepareSearch("book")
                    .setTypes("novel")
                    .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                    .setQuery(boolQuery)
                    .setFrom(0)
                    .setSize(pageSize)
                    .setScroll(timeValue);
            System.out.println(builder);
            response = builder.get();

        } else {
            response = this.client.prepareSearchScroll(scrollId)
                    .setScroll(timeValue).get();
        }

        //将scrollId返回，传入获取下一页
//        ArrayList<Map<String, Object>> result = new ArrayList<>();
        JSONObject result = new JSONObject();
        scrollId = response.getScrollId();
        result.put("scrollId", scrollId);
        JSONArray array = new JSONArray();
        for (SearchHit hit : response.getHits()) {
            array.add(hit.getSourceAsMap());
        }
        result.put("result", array);
        return new ResponseEntity(result, HttpStatus.OK);
    }

}
